
import numpy

def _parse(filename, eventSize, outcomeSize):
    '''Parse events and outcomes from a data file.'''
    with open(filename) as handle:
        for line in handle:
            try:
                fields = map(float, line.strip().split())
                #y y n y y n y y n n n n n n n
                #5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
                # These are the disabled features. Comment them out to re-enable them.
                fields[4] = 0.0
                fields[7] = 0.0
                fields[10] = 0.0
                fields[13] = 0.0
                fields[14] = 0.0
                fields[15] = 0.0
                fields[16] = 0.0
                fields[17] = 0.0
                fields[18] = 0.0
                fields[19] = 0.0
                fields[20] = 0.0
                fields[21] = 0.0
                ##fields[22] = 0.0
                fields[23] = 0.0
                fields[24] = 0.0
                assert len(fields) == 1 + eventSize

                outcome = int(fields[0]) - 0
                assert 0 <= outcome < outcomeSize

                yield outcome, numpy.array(fields[1:])
            except:
                pass


def train(learner, trainingFile, eventSize, outcomeSize):
    '''Train a learner on a set of sample data.'''
    for outcome, event in _parse(trainingFile, eventSize, outcomeSize):
        features = event
        learner.learn(features, outcome)

# Change newWay and optimism to change the defaults.
def test(learner, testFile, eventSize, outcomeSize, newWay=False, optimism=0.0):
    '''Test a learner on a randomly generated set of data.
       tp represents number of true positive(correct results)
       fp represents number of false positive(unexpected results)
       fn represents number of false negative(missing results)'''
    if newWay:
        actuallyOpenedFile = open(testFile, 'r')
        positiveCount = 0
        totalCount = 0
        for line in actuallyOpenedFile:
            totalCount += 1
            if line.startswith('1'):
                positiveCount += 1
        #print 'total:',totalCount,'positive:',positiveCount
        positiveCount += int((totalCount - positiveCount) * optimism)
        
        scoresResults = []
        correct = [0] * outcomeSize
        j = 0
        tp = 0.0
        fp = 0.0
        fn = 0.0
        for j, (outcome, event) in enumerate(_parse(testFile, eventSize, outcomeSize)):
            features = event
            pred, maxscore = learner.classify(features)
            
            #if pred == 1 and outcome == 1:
            #    tp += 1
            #elif outcome == 1:
            #    fn += 1
            #elif pred == 1:
            #    fp += 1
                
            #if pred == outcome:
            #    correct[outcome] += 1
            scoresResults.append((maxscore, outcome))
        scoresResults.sort(key=lambda x: x[0], reverse=True)
        for i in range(positiveCount):
            if scoresResults[i][1] == 1:
                tp += 1
                correct[1] += 1
            else:
                fp += 1
        for i in range(positiveCount, totalCount):
            if scoresResults[i][1] == 1:
                fn += 1
            else:
                correct[0] += 1
            
        return correct, j+1, tp, fn, fp
    else:
        correct = [0] * outcomeSize
        j = 0
        tp = 0.0
        fp = 0.0
        fn = 0.0
        
        for j, (outcome, event) in enumerate(_parse(testFile, eventSize, outcomeSize)):
            features = event
            pred, maxscore = learner.classify(features)
            
            if pred == 1 and outcome == 1:
                tp += 1
            elif outcome == 1:
                fn += 1
            elif pred == 1:
                fp += 1
                
            if pred == outcome:
                correct[outcome] += 1
        return correct, j+1, tp, fn, fp

def getFscore(precision, recall):
    if not (precision or recall):
        return 0.0
    return  2 * precision * recall / (precision + recall)
